#pragma once

#include "pch.h"

#include "Occluder.h"

GLuint VBO;
GLuint IBO;
GLuint shaderProgram;

GLuint dbgVBO;
GLuint dbgIBO;
GLuint dbgShaderProgram;
GLuint gDbgUniqueColor;

GLuint gWorldLocation;
static const char* pVS = "														\n\
#version 330																	\n\
																				\n\
layout (location = 0) in vec3 Position;											\n\
																				\n\
uniform mat4 gWorld;															\n\
out vec4 Color;																	\n\
																				\n\
void main()																		\n\
{																				\n\
	gl_Position = gWorld * vec4(Position, 1.0);									\n\
	Color = vec4(clamp(Position, 0.0, 1.0), 1.0);								\n\
}";

static const char* pFS = "														\n\
#version 330																	\n\
																				\n\
in vec4 Color;																	\n\
out vec4 FragColor;																\n\
																				\n\
void main()																		\n\
{																				\n\
	FragColor = Color;															\n\
}";

static const char* dbg_pVS = "													\n\
#version 330																	\n\
																				\n\
layout (location = 0) in vec3 Position;											\n\
																				\n\
uniform mat4 gWorld;															\n\
uniform int gDbgUniqueColor;													\n\
out vec4 Color;																	\n\
																				\n\
void main()																		\n\
{																				\n\
	gl_Position = gWorld * vec4(Position, 1.0);									\n\
	Color = vec4(1.0 * gDbgUniqueColor, 0.0, 1.0, 1.0);							\n\
}";


static void CreateVertexBufferAndIndices() {
	Vector3f Vertices[] = {
		Vector3f(-1.0f, -1.0f, 0.5773f),
		Vector3f(0.0f, -1.0f, -1.15475f),
		Vector3f(1.0f, -1.0f, 0.5773f),
		Vector3f(0.0f, 1.0f, 0.0f)
	};

	u32 Indices[] = {
		0, 3, 1,
		1, 3, 2,
		2, 3, 0,
		0, 2, 1
	};

	glGenBuffers(1, &VBO);
	glBindBuffer(GL_ARRAY_BUFFER, VBO);
	glBufferData(GL_ARRAY_BUFFER, sizeof(Vertices), Vertices, GL_STATIC_DRAW);

	glGenBuffers(1, &IBO);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO);
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(Indices), Indices, GL_STATIC_DRAW);
}

static void CreateDbgVertexBufferAndIndices() {
	Vector3f Vertices[4];
	Vertices[0] = Vector3f(-0.1f, -0.1f, 0.05773f);
	Vertices[1] = Vector3f(0.0f, -0.1f, -0.115475f);
	Vertices[2] = Vector3f(0.1f, -0.1f, 0.05773f);
	Vertices[3] = Vector3f(0.0f, 0.1f, 0.0f);

	u32 Indices[] = { 0, 3, 1,
		1, 3, 2,
		2, 3, 0,
		0, 2, 1
	};

	glGenBuffers(1, &dbgVBO);
	glBindBuffer(GL_ARRAY_BUFFER, dbgVBO);
	glBufferData(GL_ARRAY_BUFFER, sizeof(Vertices), Vertices, GL_STATIC_DRAW);

	glGenBuffers(1, &dbgIBO);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, dbgIBO);
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(Indices), Indices, GL_STATIC_DRAW);
}

inline bool InitRender() {
	// Must be done after glut is initialized!
	glewExperimental = GL_TRUE;
	GLenum res = glewInit();
	if (res != GLEW_OK) {
		fprintf(stderr, "Error: '%s'\n", glewGetErrorString(res));
		return false;
	}

	if (!GLEW_VERSION_2_1) {
		fprintf(stderr, "OpenGL 2.1 or higher required!");
		return false;
	}

	glClearColor(0.0f, 0.0f, 0.0f, 1.0f);

	return true;
}

inline void InitVertexBuffers() {
	CreateVertexBufferAndIndices();
	CreateDbgVertexBufferAndIndices();
}

inline void Render() {
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glLoadIdentity();

	static float scale = 0.0f;
	scale += 0.1f;

	glUseProgram(shaderProgram);
	u64 objects_count = 0;
	for (u64 i = 0, size = EntitiesCore::entities().size(); i < size; ++i) {
		const Entity &ent = EntitiesCore::entities()[i];
		if (!ent.needDraw()) continue;

		glUniformMatrix4fv(gWorldLocation, 1, GL_TRUE, &ent.getFinalTransform().get0());

		glEnableVertexAttribArray(0);
		glBindBuffer(GL_ARRAY_BUFFER, VBO);
		glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);

		glDrawElements(GL_TRIANGLES, 12, GL_UNSIGNED_INT, 0);

		glDisableVertexAttribArray(0);
		++objects_count;
	}

	glUseProgram(dbgShaderProgram);
	u64 dbg_objects_count = 0;
	u64 failed_tests = 0;
	for (u64 i = 0, size = EntitiesCore::dbg_entities().size(); i < size; ++i) {
		const Entity &dbg_ent = EntitiesCore::dbg_entities()[i];

		glUniformMatrix4fv(gWorldLocation, 1, GL_TRUE, &dbg_ent.getFinalTransform().get0());
		glUniform1i(gDbgUniqueColor, dbg_ent.needUniqueColor());
		if (dbg_ent.needUniqueColor())
			++failed_tests;

		glEnableVertexAttribArray(0);
		glBindBuffer(GL_ARRAY_BUFFER, dbgVBO);
		glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);

		glDrawElements(GL_TRIANGLES, 12, GL_UNSIGNED_INT, 0);

		glDisableVertexAttribArray(0);
		++dbg_objects_count;
	}
	EntitiesCore::onDbgEntitesProcessed();

	system("cls");
	std::cout << "Objects count = " << EntitiesCore::entities().size() << std::endl;
	std::cout << "Objects rendered = " << objects_count << std::endl;
	std::cout << "Dbg objects count = " << dbg_objects_count << std::endl;
	std::cout << "Failed count = " << failed_tests << std::endl;

	glFlush();
	SDL_GL_SwapWindow(g_window);
}